package com.cloudkinto.utils;

import lombok.Data;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 包裹最小三边和 在物品尺寸相同的前提下
 *
 * @author long
 */
public class PackageMinTrilateralSum {

    @Data
   public static class PackageInfo {
        /**
         * 最小三边和所有尺寸组合(去重后的)
         */
        private Set<List<Double>> packageSizeList;
        private double minTrilateralSum;
        private double weight;

        @Override
        public String toString() {
            return "PackageInfo{" +
                    "packageSizeList=" + packageSizeList +
                    ", minTrilateralSum=" + minTrilateralSum +
                    ", weight=" + weight +
                    '}';
        }
    }

    /**
     * 计算所有排列的最小三边和
     *
     * @param n      数量
     * @param length 长
     * @param width  宽
     * @param height 高
     * @return 包裹信息
     */
    public static PackageInfo caleMinTrilateralSum(int n, double length, double width, double height) {
        Set<List<Double>> permutations = generatePermutations(length, width, height);
        double minTotal = Integer.MAX_VALUE;
        PackageInfo packageInfo = null;
        for (List<Double> dims : permutations) {
            double l = dims.get(0);
            double w = dims.get(1);
            double h = dims.get(2);
            PackageInfo tempPackageInfo = calculateMinimalSumForPermutation(n, l, w, h);
            double currentMin = tempPackageInfo.getMinTrilateralSum();
            if (currentMin < minTotal) {
                minTotal = currentMin;
                packageInfo = tempPackageInfo;
            }
        }
        return packageInfo;
    }

    /**
     * 生成所有6种排列（旋转物体后的尺寸）
     */
    private static Set<List<Double>> generatePermutations(double l, double w, double h) {
        return new LinkedHashSet<List<Double>>() {{
            add(Arrays.asList(l, w, h));
            add(Arrays.asList(l, h, w));
            add(Arrays.asList(w, h, l));
//            add(Arrays.asList(w, l, h));
//            add(Arrays.asList(h, l, w));
//            add(Arrays.asList(h, w, l));
        }};
    }

    /**
     * 计算某个排列的最小三边和
     */
    private static PackageInfo calculateMinimalSumForPermutation(int n, double l, double w, double h) {
        double x0 = Math.cbrt(n * l / (w * h));
        double y0 = Math.cbrt(n * w / (l * h));

        List<Integer> xCandidates = generateCandidates(x0);
        List<Integer> yCandidates = generateCandidates(y0);

        double minSum = Integer.MAX_VALUE;

        Set<List<Double>> minSumMatchCombination = new HashSet<>();

        for (int x : xCandidates) {
            for (int y : yCandidates) {
                int productXY = x * y;
                if (productXY == 0) {
                    continue;
                }
                int zMin = Math.max((int) Math.ceil((double) n / productXY), 1);

                for (int delta = -1; delta <= 1; delta++) {
                    int z = zMin + delta;
                    z = Math.max(z, 1);
                    if (x * y * z >= n) {
                        double total = x * l + y * w + z * h;
                        if (total > minSum) {
                            continue;
                        }
                        if (total < minSum) {
                            minSum = total;
                            minSumMatchCombination.clear();
                        }
                        List<Double> arr = new ArrayList<>();
                        arr.add(x * l);
                        arr.add(y * w);
                        arr.add(z * h);
                        arr.sort(Comparator.naturalOrder());
                        minSumMatchCombination.add(arr);
                    }
                }
            }
        }
        PackageInfo packageInfo = new PackageInfo();
        packageInfo.setMinTrilateralSum(minSum);
        packageInfo.setPackageSizeList(minSumMatchCombination);
        return packageInfo;
    }

//    private static List<Integer> generateCandidates(double value) {
//        int start = Math.max(1, (int) Math.floor(value) - 1);
//        int end = (int) Math.ceil(value) + 1;
//        List<Integer> candidates = new ArrayList<>();
//        for (int i = start; i <= end; i++) {
//            if (i >= 1) {
//                candidates.add(i);
//            }
//        }
//        return candidates.stream().distinct().collect(Collectors.toList());
//    }

    /**
     * 生成排序后的候选值（更接近理论值的优先）
     * 相比上边方法稍快一点  百万调用快10%左右
     */
    private static List<Integer> generateCandidates(double value) {
        int base = (int) Math.round(value);
        List<Integer> candidates = new ArrayList<>();
        // 生成更紧凑的候选范围
        for (int i = Math.max(1, base - 2); i <= base + 2; i++) {
            candidates.add(i);
        }
        // 按与理论值的实际差距排序
        candidates.sort(Comparator.comparingDouble(i -> Math.abs(i - value)));
        return candidates.stream().distinct().collect(Collectors.toList());
    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        // 示例测试
        for (int i = 1; i <= 1; i++) {
            PackageInfo x = caleMinTrilateralSum(8, 20, 10, 9);
            System.out.println(x);
            System.out.println(x.getPackageSizeList().stream().collect(Collectors.toList()));
        }
        System.out.println("Time: " + (System.currentTimeMillis() - startTime) + "ms");
    }
}